#pragma once

#include <sstream>
#include "util.h"
#include "context.h"
#include "Md5.h"


#define ActionRegNameCombine(a,b) a##b
#define ActionRegName(a,b) ActionRegNameCombine(a,b)


inline bool CheckHeads(const std::map<std::string,std::string>& heads, const std::string& body)
{
    auto it_acc = heads.find("acc");
    if (it_acc == heads.end()) return false;
    auto it_sign = heads.find("sign");
    if (it_sign == heads.end()) return false;
    
    if (0 != Ctx.msg_router.acc.compare(it_acc->second)) return false;
    std::string acc_key = Ctx.msg_router.key;
    std::string sign_md5 = MD5(body+acc_key).toStr();
    if (0 != sign_md5.compare(it_sign->second)) return false;
    return true;
}

inline std::map<std::string,std::string> PackHeads(std::uint32_t uid, const std::string& body)
{
    std::string acc_key = Ctx.msg_router.key;
    std::string sign_md5 = MD5(body+acc_key).toStr();

    std::map<std::string,std::string> heads;
    heads["uid"] = std::to_string(uid);
    heads["tid"] = std::to_string(rand());
    heads["acc"] = Ctx.msg_router.acc;
    heads["sign"] = sign_md5;
    return heads;
}

inline std::map<std::string,std::string> PackHeads(const std::string& headers, const std::string& body)
{
    std::string strUid = "";
    
    do
    {
        Json::Reader headerReader;
        Json::Value heads_json;
        if (!headerReader.parse(headers, heads_json))
        {
            log_error("paser head json failed, headers: %s", headers.c_str());
            break;
        }
        if (!heads_json.isMember("uid") || !heads_json["uid"].isString())
        {
            log_error("uid not found: %s", headers.c_str());
            break;
        }

        strUid = heads_json["uid"].asString();
    } while (false);
    
    std::string acc_key = Ctx.msg_router.key;
    std::string sign_md5 = MD5(body+acc_key).toStr();

    std::map<std::string,std::string> heads;
    heads["uid"] = strUid;
    heads["tid"] = std::to_string(rand());
    heads["acc"] = Ctx.msg_router.acc;
    heads["sign"] = sign_md5;
    return heads;
}

template<typename T, class=typename std::enable_if<std::is_base_of<google::protobuf::Message, T>::value>::type >
inline void httpServerBack(std::uint32_t request_id, T* msg)
{
    int status_code = 404;
    std::string body;
    if (msg != nullptr)
    {
        if (!CTools::ToJson(body, *msg))
        {
            log_error("req to json failed, req:%s", msg->ShortDebugString().c_str());
        }
        else
        {
            log_debug("req:%s", msg->ShortDebugString().c_str());
            status_code = 200;
        }
    }
    std::map<std::string,std::string> heads;
    heads["Content-Type"] = "application/json";
    Ctx.msg_router.httpServerBack(request_id, status_code, heads, body);
}

template <typename Req, typename Resp>
class ActionCommon
{
public:
    static bool Init(const std::string& path, const std::string& url_path)
    {
        Ctx.msg_router.BindHttpHandle("post", path, std::bind(&ActionCommon::Handle, std::placeholders::_1, url_path));
        return true;
    }
    
public:
    static void Handle(HttpData&& http_data, const std::string& url_path)
    {
        log_debug("enter,request_id:%d", http_data.request_id);
        Req req;
        Resp resp;
        do
        {
            // json 转pb
            if (!CTools::FromJson(req, http_data.body))
            {
                log_error("json parser failed,url:%s,body:%s", http_data.target.c_str(), http_data.body.c_str());
                break;
            }
                 
            // pb转字节流
            std::string body = "";
            req.SerializeToString(&body);

            // 签名
            std::map<std::string,std::string> heads = PackHeads(http_data.heads, body); //PackHeads(req.uid(), body);
            heads["Content-Type"] = "application/json";
            
            std::string url = Ctx.msg_router.url + url_path;
            std::uint32_t event_id = 0;
            // 请求http网络
            if (!Ctx.msg_router.sendHttpPOST(url, body, heads, event_id) || (event_id == 0))
            {
                log_error("net post failed,url:%s,param:%s", url.c_str(),http_data.body.c_str());
                break;
            }
            log_debug("http post,url:%s,param:%s", url.c_str(), http_data.body.c_str());
            // 添加回调监听
            Ctx.msg_router.AddCurlEvent(event_id, 
                [request_id = http_data.request_id]
                (std::string& body, bool is_ok){
                log_debug("enter,request_id:%d", request_id);
                Resp resp;
                do
                {
                    if (!resp.ParseFromArray(body.data(), body.size()))
                    {
                        break;
                    }
                } while (false);
                httpServerBack(request_id, &resp);
            });
            return;
        } while (false);
        httpServerBack(http_data.request_id, &resp);
    }
};

#define ActionReg(path, url_path, Req, Resp) static bool ActionRegName(__init__,__LINE__) = ActionCommon<Req, Resp>::Init(path, url_path)







